package lv.senan.algorithms.turney;

import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import lv.semti.morphology.analyzer.Analyzer;
import lv.semti.morphology.analyzer.Wordform;
import lv.senan.clients.manatee.ConnectionSettings;
import lv.senan.clients.manatee.ManateeClient;
import lv.senan.morphology.WordText;
import lv.senan.morphology.Word;
import lv.senan.morphology.WordProperties;

/**
 * Creates complete set of text properties.
 * 
 * @author Igors Gulbinskis
 */
public class TextProcessor {
		
	private static Logger logger = Logger.getLogger(TextProcessor.class.getName());
		
	final String SPECIAL_CHARACTERS[] = {",", ";", ".", "!", "?", ":", "/",
		"\"", "@", "[", "]", "{", "}", "(", ")", "+", "-", ":", "'", "|", "*",
		"&", "#", "$", "^"};
	
	private ConnectionSettings settings;
	private ManateeClient      client;
	private Analyzer           morph;
		
	public TextProcessor() {
		settings = new ConnectionSettings("#######.#####.##", 8080, "demo", "korpuss", "timeklis-1.0");
		client = new ManateeClient(settings);
		try {
			morph = new Analyzer("lib/lexicon.xml");
			
			morph.enableDiminutive = true;
			morph.enablePrefixes   = true;
			morph.enableVocative   = false;
			morph.enableGuessing   = false;
		} catch (Exception e) {
			logger.warning(e.getMessage());
		}
	}
	
	/**
	 * Checking for special characters as commas.
	 */
	private boolean containsSpecialCharacters(String word) {				
		for (int i = 0; i < SPECIAL_CHARACTERS.length; i++) {
			if (word.indexOf(SPECIAL_CHARACTERS[i]) != -1) {
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * Replacing of special characters. It is important because from Manatee server
	 * it is important to capture pure words.
	 */
	private String replaceSpecialCharacters(String word) {
		String result = word;
		for (int i = 0; i < SPECIAL_CHARACTERS.length; i++) {
			result = result.replace(SPECIAL_CHARACTERS[i], "");
		}
		
		return result;
	}
	
	/**
	 * Gets properties tag from string line returned by Manatee.
	 */
	private String getWordProperties(String line, String word) {
		if (line != null) {
			try {
				Pattern pattern1 = Pattern.compile(word.toLowerCase() + ".*/.+\\sattr");
				Pattern pattern2 = Pattern.compile("/.+\\s");
				Matcher matcher1 = pattern1.matcher(line.toLowerCase());
				
				if (matcher1.find()) {
					String localized = line.substring(matcher1.start(), matcher1.end());
					Matcher matcher2 = pattern2.matcher(localized.toLowerCase());
					if (matcher2.find()) {				
						return localized.substring(matcher2.start() + 1, matcher2.end() - 1).split("\\s")[0];
					}
				}
			} catch (Exception e) {
				return "-";
			}
		}
		
		return null;
	}
	
	/**
	 * Maps Manatee properties tag to appropriate word type.
	 */
	private Word getWordFromProperties(String characters, String properties) {
		int type = WordProperties.UNKNOWN;
		if (properties != null) {			
			if (properties.indexOf("n") == 0) {
				type = WordProperties.NOUN;
			} else if (properties.indexOf("v") == 0) {
				type = WordProperties.VERB;
			} else if (properties.indexOf("a") == 0) {
				type = WordProperties.ADJECTIVE;
			} else if (properties.indexOf("r") == 0) {
				type = WordProperties.ADVERB;
			}
		}
		
		return new Word(characters, type);
	}
	
	private Word getWord(String characters) {
		ArrayList<Wordform> proposals = morph.analyze(characters).wordforms;
		int type = WordProperties.UNKNOWN;
		if (proposals.size() > 0) {
			boolean found = false;
			for (Wordform proposal : proposals) {
				if ("Īpašības vārds".equals(proposal.getValue("Vārdšķira"))) {
					type = WordProperties.ADJECTIVE;
					found = true;
					break;
				}
			}
			if (!found) {
				if ("Vietniekvārds".equals(proposals.get(0).getValue("Vārdšķira"))
					|| "Lietvārds".equals(proposals.get(0).getValue("Vārdšķira"))) {
					type = WordProperties.NOUN;
				} else if ("Īpašības vārds".equals(proposals.get(0).getValue("Vārdšķira"))) {
					type = WordProperties.ADJECTIVE;
				} else if ("Apstākļa vārds".equals(proposals.get(0).getValue("Vārdšķira"))) {
					type = WordProperties.ADVERB;
				} else if ("Darbības vārds".equals(proposals.get(0).getValue("Vārdšķira"))
					&& "Divdabis".equals(proposals.get(0).getValue("Izteiksme"))
					&& "Darāmā".equals(proposals.get(0).getValue("Kārta"))) {
					type = WordProperties.PARTICLE;
				} else if ("Darbības vārds".equals(proposals.get(0).getValue("Vārdšķira"))) {
					type = WordProperties.VERB;
				}
			}
		}
		
		return new Word(characters, type);
	}
	
	/**
	 * Physical connection to Manatee server occurs here.
	 */
	private String getPartOfSpeechProperties(String partOfSpeech) {
		if (partOfSpeech != null) {
			try {			
				client.connect();
				String result[] = client.getResults(partOfSpeech, "word,tag -", 0, 1);
				return "".equals(result[0]) || result == null ? null : result[0];
			} catch (Exception e) {
				logger.warning(e.getMessage());
				e.printStackTrace();
			} finally {
				client.disconnect();
			}
		}
		
		return null;
	}	
	
	/**
	 * Receives text and returns the same text but with properties of each word. By properties
	 * is meant something like word type.
	 */
	public WordText process(String text, boolean corpusUsed) {
		String words[] = text.split("\\s+");
		WordText result = new WordText(words.length);
				
		String w1 = null;
		String w2 = null;		
		for (int i = 0; i < words.length; i++) {			
			w1 = words[i];	
			logger.info("Processing word " + (i + 1) + "/" + words.length);
			if (corpusUsed) {
				if (i < words.length - 1) {
					w2 = words[i + 1];
				}
				
				String partOfSpeech = null;
				if (!containsSpecialCharacters(w1)) {
					if (w2 != null) {
						w1 = replaceSpecialCharacters(w1);
						w2 = replaceSpecialCharacters(w2);
						partOfSpeech = "\"" + w1 +"\" \""+ w2 + "\"";
					} else {
						partOfSpeech = "\"" + w1 +"\"";
					}
				} else {
					w1 = replaceSpecialCharacters(w1);
					partOfSpeech = "\"" + w1 +"\"";
				}
				
				logger.log(Level.FINEST, "current pos = " + partOfSpeech);
				
				if (partOfSpeech != null) {				
					String partOfSpeechProperties = getPartOfSpeechProperties(partOfSpeech);				
					if (partOfSpeechProperties == null) {
						partOfSpeech = "\"" + w1 +"\"";
						partOfSpeechProperties = getPartOfSpeechProperties(partOfSpeech);
						if (!(result.get(i).getCharacters().equals(w1)
							&& result.get(i).getType() != WordProperties.UNKNOWN)) {
							result.set(i, getWordFromProperties(w1, getWordProperties(partOfSpeechProperties, w1)));
						}
					} else {
						if (!(result.get(i).getCharacters().equals(w1)
							&& result.get(i).getType() != WordProperties.UNKNOWN)) {
							result.set(i, getWordFromProperties(w1, getWordProperties(partOfSpeechProperties, w1)));
						}
						if (w2 != null) {
							result.set(i + 1, getWordFromProperties(w2, getWordProperties(partOfSpeechProperties, w2)));
						}
					}
				}
				
				w1 = null;
				w2 = null;
			} else {
				result.set(i, getWord(replaceSpecialCharacters(w1)));
			}
		}
		
		return result;
	}
	
}
